#ifndef Chromosome_header_file
#define Chromosome_header_file

#include "Data.h"
#include "RandomNumberGenerator.h"
#include <iostream>
#include <fstream>
#include <iomanip>

class Chromosome {

public:

    static int length;

    static Chromosome* generateRandomChromosome() {
        auto chromosome = new Chromosome();
        auto randomNumberGenerator = RandomNumberGenerator::getRandomNumberGenerator();
        for (int i = 0; i < length; i++) {
            chromosome->setGene(i, randomNumberGenerator->getRandomNumber(-1.0, 1.0));
        }
        return chromosome;
    }

    double getFitness() {
        if (fitness >= 0) {
            return fitness;
        }
        double sum, err = 0;
        for (int i = length; i < testDataTotal; i++) {
            sum = 0;
            for (int j = 0; j < length; j++) {
                sum += testData[i - j - 1] * genes[j];
            }
            err += (testData[i] - sum) * (testData[i] - sum) / (testDataTotal - length);
        }
        fitness = 1.0 / (err + 1.0);
        return fitness;
    }

    Chromosome* cross(Chromosome* anotherChromosome) {
        auto newChromosome = new Chromosome();
        auto randomNumberGenerator = RandomNumberGenerator::getRandomNumberGenerator();
        for (int i = 0; i < length; i++) {
            if (randomNumberGenerator->getRandomNumber(-1.0, 1.0) > 0.0) {
                newChromosome->setGene(i, genes[i]);
            } else {
                newChromosome->setGene(i, anotherChromosome->getGene(i));
            }
        }
        return newChromosome;
    }

    void varies(double p) {
        auto randomNumberGenerator = RandomNumberGenerator::getRandomNumberGenerator();
        for (int i = 0; i < length; i++) {
            if (randomNumberGenerator->getRandomNumber(0.0, 1.0) <= p) {
                genes[i] += randomNumberGenerator->getRandomNumber(-0.1, 0.1);
            }
        }
    }

    ~Chromosome() {
        delete[] genes;
    }

    double getGene(int i) {
        return genes[i];
    }

    void setGene(int i, double value) {
        genes[i] = value;
    }

    void dump() {
        using namespace std;
        cout << "AR params:p1~p" << length << endl;
        for (int i = 0; i < length; i++) {
            cout << setprecision(9) << genes[i];
            if (i != length - 1) {
                cout << ",";
            }
        }
        cout << endl;
    }

    void createRemanentErrorFile(std::string& fileName) {
        using namespace std;
        ofstream errorFile(fileName);
        if (!errorFile.is_open()) {
            cout << "Warning: Open \"" << fileName << "\" fail." << endl;
            return;
        }
        errorFile << fixed << setprecision(9);
        double sum = 0;
        for (long i = length; i < testDataTotal; i++) {
            sum = 0;
            for (long j = 0; j < length; j++) {
                sum += testData[i - j - 1] * genes[j];
            }
            errorFile << (testData[i] - sum) << endl;
        }
    }

    double getStdError() {
        using namespace std;
        vector<double> e;
        double sum = 0, err, errSum = 0;
        double total = testDataTotal - length;
        for (long i = length; i < testDataTotal; i++) {
            sum = 0;
            for (long j = 0; j < length; j++) {
                sum += testData[i - j - 1] * genes[j];
            }
            err = testData[i] - sum;
            errSum += err;
            e.emplace_back(err);
        }
        double avgValue = errSum / total;
        sum = 0;
        for (auto er : e) {
            sum += (er - avgValue) * (er - avgValue);
        }
        return sqrt(sum / total);
    }

protected:

    Chromosome() {
        genes = new double[length];
    }

private:

    double* genes;

    double fitness = -1.0;

};

int Chromosome::length = 5;

#endif
